import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:flutter_repository/annotations/repository.dart';
import 'package:source_gen/source_gen.dart' as MyBuilder;
import 'package:path/path.dart' as Path;

class RepositoryGenerator extends MyBuilder.GeneratorForAnnotation<Repository> {
  @override
  String generateForAnnotatedElement(Element element,
      MyBuilder.ConstantReader annotation, BuildStep buildStep) {
    if (element is! ClassElement) {
      final name = element.displayName;
      throw MyBuilder.InvalidGenerationSourceError(
        'Generator cannot target `$name`.',
        todo: 'Remove the [Repository] annotation from `$name`.',
      );
    }
    var parOf = "part of '${Path.basename(buildStep.inputId.path)}';";
    return [_implementClass(element, annotation)].join("\n\n");
  }

  String _implementClass(
      ClassElement element, MyBuilder.ConstantReader? annotation) {
    final emitter = DartEmitter();
    //文件名
    //原来的类名
    final className = element.name;

    final classBuilder = Mixin((c) {
      c
        ..name = '_$className'
        ..types.addAll(element.typeParameters.map((e) => refer(e.name)));
      c.methods.addAll(_parseMethods(element));
      c.on = Reference(_generateTypeParameterizedName(element));
    });
    return DartFormatter().format([
      "class ${className}Impl = $className with _$className;",
      classBuilder.accept(emitter)
    ].join('\n\n'));
  }

  String _generateTypeParameterizedName(TypeParameterizedElement element) =>
      element.displayName +
      (element.typeParameters.isNotEmpty
          ? '<${element.typeParameters.join(',')}>'
          : '');

  List<Method> _parseMethods(ClassElement element) {
    List<Method> methods = [];

    ///添加方法
    var list = element.methods.where((element) {
      ///过滤出使用了ProxyCall注解的方法
      final proxyCallChecker = _typeChecker(ProxyCall);
      final annot =
          proxyCallChecker.firstAnnotationOf(element, throwOnUnresolved: false);
      return annot != null;
    }).map<Method>((m) {
      Method method = Method((mm) {
        mm
          ..name = m.displayName
          ..returns = refer(_displayString(m.returnType))
          ..annotations.add(const CodeExpression(Code('override')));

        /// required parameters
        mm.requiredParameters.addAll(m.parameters
            .where((it) => it.isRequiredPositional)
            .map((it) => Parameter((p) => p
              ..name = it.name
              ..type = refer(_displayString(it.type,
                  withNullability:
                      it.type.nullabilitySuffix == NullabilitySuffix.question))
              ..named = it.isNamed)));

        /// optional positional or named parameters
        mm.optionalParameters.addAll(
          m.parameters.where((i) => i.isOptional || i.isRequiredNamed).map(
            (it) {
              print(
                  "${it.name}类型:${it.type.nullabilitySuffix} 是否命名canshu:${it.isNamed}");
              return Parameter((p) => p
                ..required = (it.isNamed &&
                    it.type.nullabilitySuffix == NullabilitySuffix.none &&
                    !it.hasDefaultValue)
                ..name = it.name
                ..named = it.isNamed
                ..type = refer(_displayString(it.type,
                    withNullability: it.type.nullabilitySuffix ==
                        NullabilitySuffix.question))
                ..defaultTo = it.defaultValueCode == null
                    ? null
                    : Code(it.defaultValueCode!));
            },
          ),
        );

        mm.body = _methodBody(m, element);
      });
      return method;
    }).toList();
    methods.addAll(list);
    return methods;
  }

  Code _methodBody(MethodElement m, ClassElement c) {
    final blocks = <Code>[];
    String args = m.parameters
        .map((e) => e.isNamed ? "${e.name}:${e.name}" : e.name)
        .join(',');
    final repositoryChecker = _typeChecker(Repository);
    final repoAnnot =
        repositoryChecker.firstAnnotationOf(c, throwOnUnresolved: false)!;
    final obj = MyBuilder.ConstantReader(repoAnnot);
    final streamWrapper = obj.read("streamWrapper")!;
    final futureWrapper = obj.read("futureWrapper")!;
    final streamWrapperName =
        streamWrapper.objectValue.toFunctionValue()!.qualifiedName;
    final futureWrapperName =
        futureWrapper.objectValue.toFunctionValue()!.qualifiedName;

    ///判断是否开启重试
    bool retry = false;
    final retryChecker = _typeChecker(Retry);
    final retryAnnot =
        retryChecker.firstAnnotationOf(m, throwOnUnresolved: false);
    retry = retryAnnot != null;

    ///判断返回值是future还是Stream
    bool isStream = _typeChecker(Stream).isExactly(m.returnType.element!);

    ///判断是异步的方式
    bool isFuture = _typeChecker(Future).isExactly(m.returnType.element!);

    ///普通方法
    bool isNormal = !isStream && !isFuture;

    ///
    if (isStream) {
      blocks.add(refer(streamWrapperName)
          .call([
            refer("() => super.${m.name}($args)"),
            if (retry) refer("retry:true")
          ])
          .returned
          .statement);
    }
    if (isFuture) {
      blocks.add(refer(futureWrapperName)
          .call([
            refer("() => super.${m.name}($args)"),
            if (retry) refer("retry:true")
          ])
          .returned
          .statement);
    }

    return Block.of(blocks);
  }

  String _displayString(dynamic e, {bool withNullability = false}) {
    try {
      return e.getDisplayString(withNullability: withNullability);
    } catch (error) {
      if (error is TypeError) {
        return e.getDisplayString();
      } else {
        rethrow;
      }
    }
  }

  //获取泛型，比如Future<String> 这里获取的就是String
  DartType? _genericOf(DartType type) {
    return type is InterfaceType && type.typeArguments.isNotEmpty
        ? type.typeArguments.first
        : null;
  }

  MyBuilder.TypeChecker _typeChecker(Type type) =>
      MyBuilder.TypeChecker.fromRuntime(type);
}

// Builder generatorFactoryBuilder(BuilderOptions options) => SharedPartBuilder(
//     [RepositoryGenerator()], "x");
Builder generatorFactoryBuilder(BuilderOptions options) =>
    MyBuilder.SharedPartBuilder([RepositoryGenerator()], "flutter_repository");

extension ExecutableElementExtension on ExecutableElement {
  /// Returns the name of `this` qualified with the class name if it's a
  /// [MethodElement].
  String get qualifiedName {
    if (this is FunctionElement) {
      return name;
    }

    if (this is MethodElement) {
      return '${enclosingElement.name}.$name';
    }

    throw UnsupportedError(
      'Not sure how to support typeof $runtimeType',
    );
  }
}
